package com.dbsync.core;

import com.dbsync.exception.DbsyncException;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * 配置文件加载类
 */
public class ConfigLoader {
    // 配置文件数据
    private static Config config;

    private ConfigLoader() {}

    /**
     * 初始化
      * @return 返回config对象
     * @throws Exception
     */
    public static Config init() throws Exception {
        if(config==null||isHotLoad()){
            return loadConfig();
        }else{
            return config;
        }
    }

    /**
     * 读取配置文件
     * @return 返回config对象
     * @throws Exception
     */
    private static Config loadConfig() throws Exception{
        if (config == null){
            config = new Config();
        }
        SAXReader reader = new SAXReader();
        InputStream in = ConfigLoader.class.getClassLoader().getResourceAsStream("dbConfig.xml");
        Document document = reader.read(in);
        Element root = document.getRootElement();

        // 根节点下所有子节点
        Map<String, Element> rootElements = getElementMap(root);
        config.setHotload(Boolean.valueOf(rootElements.getOrDefault("hotload", null).getTextTrim()));

        // db节点
        Element dbEL = rootElements.get("database");
        if (dbEL == null){
            throw new DbsyncException("\"<database></database>\"标签未找到，该标签为必须项，请检查配置文件！");
        }
        // db节点数据
        Map<String, String> dbData = getElementValue(dbEL);
        checkElement(dbData, "<database></database>");
        Database database = new Database(dbData.get("url"), dbData.get("username"), dbData.get("password"), dbData.get("driverClassName"));
        config.setDatabase(database);

        // 任务节点
        Element targetsEL = rootElements.get("targets");
        if (targetsEL == null){
            throw new DbsyncException("\"<targets></targets>\"标签未找到，该标签为必须项，请检查配置文件！");
        }
        // 任务节点集合
        List<Element> targetList = getElementList(targetsEL);
        if (targetList.isEmpty()){
            throw new DbsyncException("\"<target></target>\"标签未找到，该标签为必须项，请检查配置文件！");
        }
        // 任务节点数据集合
        Set<Target> targetDataSet = new HashSet<Target>();
        List<Target> targetDataList = new ArrayList<Target>();
        SimpleDateFormat format = new SimpleDateFormat("HH:mm");
        for (Element targetEL : targetList){
            Map<String, String> targetData = getElementValue(targetEL);
            checkElement(targetData, "<target></target>");
            StringBuilder tables = new StringBuilder();
            if (targetData.get("tables")==null||targetData.get("tables").trim().length()==0){
                tables.append("allTables");
            }else{
                tables.append(targetData.get("tables"));
            }
            Date date = null;
            if (targetData.get("time")!=null&&targetData.get("time").trim().length()>0){
                date = format.parse(targetData.get("time"));
            }else{
                date = format.parse("00:00");
            }
            try {
                Calendar calendar = Calendar.getInstance();
                int year = calendar.get(Calendar.YEAR);
                int month = calendar.get(Calendar.MONTH);
                int day = calendar.get(Calendar.DAY_OF_MONTH);
                calendar.setTime(date);
                calendar.set(Calendar.YEAR, year);
                calendar.set(Calendar.MONTH, month);
                calendar.set(Calendar.DAY_OF_MONTH, day);
                date = calendar.getTime();
            }catch (Exception e){
                throw new DbsyncException("\"<target></target>\"标签下\"<time></time>\"标签内容错误，格式应为：\"HH:mm\"，请检查配置文件！");
            }
            Target target = new Target(targetData.get("url"), targetData.get("username"), targetData.get("password"), targetData.get("driverClassName"), Arrays.asList(tables.toString().split(",")), date);
            targetDataSet.add(target);
        }
        targetDataList.addAll(targetDataSet);
        config.setTargets(targetDataList);

        if (in != null){
            in.close();
        }
        return config;
    }

    /**
     * 获取xml节点下所有子节点
     * @param node 节点
     * @return 返回Map集合
     */
    private static Map<String, Element> getElementMap(Element node){
        Map<String, Element> map = new HashMap<String, Element>();
        Iterator<Element> iterator = node.elementIterator();
        while (iterator.hasNext()){
            Element el = iterator.next();
            map.put(el.getName(), el);
        }
        return map;
    }

    /**
     * 获取xml节点下所有子节点
     * @param node 节点
     * @return 返回List集合
     */
    private static List<Element> getElementList(Element node){
        List<Element> list = new ArrayList<Element>();
        Iterator<Element> iterator = node.elementIterator();
        while (iterator.hasNext()){
            Element el = iterator.next();
            list.add(el);
        }
        return list;
    }

    /**
     * 获取xml节点下所有子节点数据
     * @param node 节点
     * @return 返回Map集合数据
     */
    private static Map<String, String> getElementValue(Element node){
        Map<String, String> data = new HashMap<String, String>();
        Iterator<Element> iterator = node.elementIterator();
        while (iterator.hasNext()){
            Element el = iterator.next();
            data.put(el.getName(), el.getTextTrim());
        }
        return data;
    }

    /**
     * 判断是否为热加载
     * @return 返回boolean类型
     */
    private static boolean isHotLoad(){
        return config.getHotload();
    }

    /**
     * 检查必要项
     * @param data 数据
     * @param elname 标签名称
     * @throws DbsyncException
     */
    private static void checkElement(Map<String, String> data, String elname) throws DbsyncException{
        if (data.get("driverClassName") == null){
            throw new DbsyncException("\""+elname+"\"标签下\"<driverClassName></driverClassName>\"标签未找到，该标签为必须项，请检查配置文件！");
        }
        if (data.get("url") == null){
            throw new DbsyncException("\""+elname+"\"标签下\"<url></url>\"标签未找到，该标签为必须项，请检查配置文件！");
        }
        if (data.get("username") == null){
            throw new DbsyncException("\""+elname+"\"标签下\"<username></username>\"标签未找到，该标签为必须项，请检查配置文件！");
        }
        if (data.get("password") == null){
            throw new DbsyncException("\""+elname+"\"标签下\"<password></password>\"标签未找到，该标签为必须项，请检查配置文件！");
        }
    }

}
